import { Device, parseScalabilityMode } from "mediasoup-client";
import { Peer, WebSocketTransport } from "protoo-client";
import { useCallback, useEffect, useRef, useState } from "react";
import store from '@/store';

type ProduceType = "audio" | "video" | "data";
type ProducersMap = { [key in ProduceType]?: any };

/**
 * Hook: 建立 protoo 信令连接并初始化 mediasoup Device
 * @param url WebSocket 信令地址
 * @param onRequest 可选的请求回调
 */
export const useProtoo = (url: string, onRequest?: (data: any) => void) => {
  const [protooPeer, setProtooPeer] = useState<Peer | null>(null);
  const [mediasoupDevice, setMediasoupDevice] = useState<Device | null>(null);
  const [remotePeers, setRemotePeers] = useState<any[]>([]);

  // 初始化 mediasoup Device 并 join 房间
  const initDeviceAndJoin = useCallback(async (peer: Peer) => {
    // 获取 SFU Router 能力
    const routerRtpCapabilities = await peer.request("getRouterRtpCapabilities");

    // 创建并加载 Device
    const device = await Device.factory();
    await device.load({
      routerRtpCapabilities,
      preferLocalCodecsOrder: true
    });

    // 发送 join 请求
    const { peers } = await peer.request("join", {
      displayName: device.handlerName,
      device,
      rtpCapabilities: device.rtpCapabilities,
      sctpCapabilities: device.sctpCapabilities
    });

    console.log("当前房间已有 peers:", peers);
    const tmpList = peers.map((peer: any) => {
      return { ...peer, consumers: [], dataConsumers: [] }
    });
    setRemotePeers(tmpList);
    setMediasoupDevice(device);
  }, []);

  useEffect(() => {
    const wsTransport = new WebSocketTransport(url);
    const peer = new Peer(wsTransport);

    peer.on("open", () => {
      setProtooPeer(peer);
      initDeviceAndJoin(peer);
      console.log("Protoo 连接已建立");
    });

    peer.on("close", () => console.log("Protoo 连接关闭"));
    peer.on("disconnected", () => console.log("Protoo 连接断开"));
    peer.on("failed", () => console.log("Protoo WebSocket 失败"));

    peer.on("request", (request, accept, reject) => {
      try {
        console.log("收到 Protoo 请求:", request);
        onRequest?.(request);
        accept();
      } catch (err) {
        console.error(err);
        reject();
      }
    });

    peer.on("notification", (notification) => {
      switch (notification.method) {
        case 'peerClosed':
          console.log('peerClosed', remotePeers, notification.data.peerId)
          setRemotePeers((prevPeers) => prevPeers.filter(peer => peer.id !== notification.data.peerId));
          break
        case 'mediasoup-version': {
          const { version } = notification.data;
          console.log('mediasoup-version', version);

          // store.dispatch(stateActions.setMediasoupVersion(version));

          break;
        }

        case 'producerScore': {
          const { producerId, score } = notification.data;
          console.log('producerScore', producerId, score);
          break;
        }

        case 'newPeer': {
          const peer = notification.data;
          setRemotePeers(prevPeers => [...prevPeers, { ...peer, consumers: [], dataConsumers: [] }]);


          break;
        }

        case 'peerClosed': {
          const { peerId } = notification.data;
          setRemotePeers(prevPeers => prevPeers.filter(peer => peer.peerId !== peerId));

          // store.dispatch(stateActions.removePeer(peerId));

          break;
        }

        case 'peerDisplayNameChanged': {
          const { peerId, displayName, oldDisplayName } = notification.data;
          setRemotePeers(prevPeers =>
            prevPeers.map(peer => (peer.peerId === peerId ? { ...peer, displayName } : peer))
          );

          // store.dispatch(stateActions.setPeerDisplayName(displayName, peerId));


          break;
        }

        case 'downlinkBwe': {
          console.log('downlinkBwe', notification.data);

          break;
        }

        case 'consumerClosed': {
          const { consumerId } = notification.data;
          console.log('consumerClosed', notification.data);
          // const consumer = .get(consumerId);


          // store.dispatch(stateActions.removeConsumer(consumerId, peerId));

          break;
        }

        case 'consumerPaused': {
          const { consumerId } = notification.data;
          // const consumer = this._consumers.get(consumerId);

          // if (!consumer) break;

          // consumer.pause();

          // store.dispatch(stateActions.setConsumerPaused(consumerId, 'remote'));

          break;
        }

        case 'consumerResumed': {
          const { consumerId } = notification.data;
          console.log('consumerResumed', consumerId);
          // const consumer = this._consumers.get(consumerId);

          // if (!consumer) break;

          // consumer.resume();

          // store.dispatch(stateActions.setConsumerResumed(consumerId, 'remote'));

          break;
        }

        case 'consumerLayersChanged': {
          const { consumerId, spatialLayer, temporalLayer } = notification.data;
          console.log('consumerLayersChanged', consumerId, spatialLayer, temporalLayer);
          // const consumer = this._consumers.get(consumerId);

          // if (!consumer) break;

          // store.dispatch(
          //   stateActions.setConsumerCurrentLayers(
          //     consumerId,
          //     spatialLayer,
          //     temporalLayer
          //   )
          // );

          break;
        }

        case 'consumerScore': {
          const { consumerId, score } = notification.data;
          console.log('consumerScore', consumerId, score);

          // store.dispatch(stateActions.setConsumerScore(consumerId, score));

          break;
        }

        case 'dataConsumerClosed': {
          const { dataConsumerId } = notification.data;
          console.log('dataConsumerClosed', dataConsumerId);
          // const dataConsumer = this._dataConsumers.get(dataConsumerId);

          // if (!dataConsumer) break;

          // dataConsumer.close();
          // this._dataConsumers.delete(dataConsumerId);

          // const { peerId } = dataConsumer.appData;

          // store.dispatch(
          //   stateActions.removeDataConsumer(dataConsumerId, peerId)
          // );

          break;
        }

        case 'activeSpeaker': {
          const { peerId } = notification.data;
          console.log('activeSpeaker', peerId);
          // setRemotePeers

          // store.dispatch(stateActions.setRoomActiveSpeaker(peerId));

          break;
        }

        default: {
          console.error(
            'unknown protoo notification.method "%s"',
            notification.method
          );
        }
      }
    });

    return () => {
      peer.close();
    };
  }, []);

  return { protooPeer, mediasoupDevice, remotePeers, setRemotePeers };
};

/**
 * Hook: 创建发送端 Transport 并管理 Producer
 */
export const useSendTransport = (protooPeer: Peer | null, device: Device | null, isProducer: boolean) => {
  const sendTransportRef = useRef<any>(null);
  const producersRef = useRef<ProducersMap>({});

  // 创建发送端 Transport
  const createSendTransport = useCallback(async () => {
    if (!protooPeer || !device) return;

    const transportInfo = await protooPeer.request("createWebRtcTransport", {
      forceTcp: false,
      producing: true,
      consuming: false,
      sctpCapabilities: device.sctpCapabilities
    });

    const { id, iceParameters, iceCandidates, dtlsParameters, sctpParameters } = transportInfo;

    sendTransportRef.current = device.createSendTransport({
      id,
      iceParameters,
      iceCandidates,
      dtlsParameters: { ...dtlsParameters, role: "auto" },
      sctpParameters,
      iceServers: []
    });

    // 连接事件
    sendTransportRef.current.on("connect", ({ dtlsParameters }: any, callback: () => void, errback: (err: Error) => void) => {
      protooPeer
        .request("connectWebRtcTransport", {
          transportId: sendTransportRef.current.id,
          dtlsParameters
        })
        .then(callback)
        .catch(errback);
    });

    // produce 事件
    sendTransportRef.current.on("produce", async ({ kind, rtpParameters, appData }: any, callback: (res: any) => void, errback: (err: Error) => void) => {
      try {
        const { id } = await protooPeer.request("produce", {
          transportId: sendTransportRef.current.id,
          kind,
          rtpParameters,
          appData
        });
        callback({ id });
      } catch (err) {
        errback(err as Error);
      }
    });
  }, [protooPeer, device]);

  // 创建 Producer
  const createProducer = useCallback(async (track: MediaStreamTrack, type: ProduceType) => {
    if (!sendTransportRef.current) return;
    console.log("创建 Producer:", type);
    const producer = await sendTransportRef.current.produce({ track });
    producersRef.current[type] = producer;
  }, []);

  useEffect(() => {
    createSendTransport();
  }, [createSendTransport]);

  return { sendTransportRef, producersRef, createProducer };
};

/**
 * Hook: 创建接收端 Transport 并管理 Consumer
 */
export const useRecvTransport = (protooPeer: Peer | null, device: Device | null, setPeers: React.Dispatch<React.SetStateAction<any[]>> // ✅ Correct type
) => {
  const recvTransportRef = useRef<any>(null);
  const consumersRef = useRef<Map<string, any>>(new Map());
  const dataConsumersRef = useRef<Map<string, any>>(new Map());
  const nextDataChannelTestNumber = useRef(0);

  // 创建接收端 Transport
  const createRecvTransport = useCallback(async () => {
    if (!protooPeer || !device) return;

    const transportInfo = await protooPeer.request("createWebRtcTransport", {
      forceTcp: false,
      producing: false,
      consuming: true,
      sctpCapabilities: device.sctpCapabilities
    });

    const { id, iceParameters, iceCandidates, dtlsParameters, sctpParameters } = transportInfo;
    // ⚠️ 接收端必须用 createRecvTransport
    recvTransportRef.current = device.createRecvTransport({
      id,
      iceParameters,
      iceCandidates,
      dtlsParameters: { ...dtlsParameters, role: "auto" },
      sctpParameters,
      iceServers: []
    });

    recvTransportRef.current.on("connect", ({ dtlsParameters }: any, callback: () => void, errback: (err: Error) => void) => {
      console.log("接收端 Transport 连接中...");
      protooPeer
        .request("connectWebRtcTransport", {
          transportId: recvTransportRef.current.id,
          dtlsParameters
        })
        .then(callback)
        .catch(errback);
    });

  }, [protooPeer, device]);

  const createConsumer = async (request: any) => {
    switch (request.method) {
      case 'newConsumer': {

        const {
          peerId,
          producerId,
          id,
          kind,
          rtpParameters,
          type,
          appData,
          producerPaused,
        } = request.data;

        try {
          const consumer = await recvTransportRef.current.consume({
            id,
            producerId,
            kind,
            rtpParameters,
            // NOTE: Force streamId to be same in mic and webcam and different
            // in screen sharing so libwebrtc will just try to sync mic and
            // webcam streams from the same remote peer.
            streamId: `${peerId}-${appData.share ? 'share' : 'mic-webcam'}`,
            appData: { ...appData, peerId }, // Trick.
          });

          // Store in the map.
          consumersRef.current.set(consumer.id, consumer);
          const { video, audio } = consumer.track.mediaStreamTrack.getSettings();
          console.log(
            `Consumer ${consumer.id} created: ${consumer.track.kind} (${video?.width}x${video?.height}, ${audio?.sampleRate}hz)`,
            consumer
          );
          // console.log("consumer", consumer)
          const stream = new MediaStream()
          stream.addTrack(consumer.track);
          setPeers((peers: any) => {
            const peer = peers.findIndex((peer: { peerId: any; }) => peer.peerId === consumer.appData.peerId)
            if (peer === -1) return peers
            const tmp = [...peers]
            tmp[peer] = { ...tmp[peer], consumers: [...tmp[peer].consumers, consumer] }
            return tmp
          })

          consumer.on('transportclose', () => {
            consumersRef.current.delete(consumer.id);
          });

          const { spatialLayers, temporalLayers } =
            parseScalabilityMode(
              consumer.rtpParameters.encodings[0].scalabilityMode
            );
          console.log("创建 Consumer:", consumer);


          // We are ready. Answer the protoo request so the server will


          // If audio-only mode is enabled, pause it.

        } catch (error) {




          throw error;
        }

        break;
      }

      case 'newDataConsumer': {


        const {
          peerId, // NOTE: Null if bot.
          dataProducerId,
          id,
          sctpStreamParameters,
          label,
          protocol,
          appData,
        } = request.data;

        try {
          const dataConsumer = await recvTransportRef.current.consumeData({
            id,
            dataProducerId,
            sctpStreamParameters,
            label,
            protocol,
            appData: { ...appData, peerId }, // Trick.
          });

          // Store in the map.
          dataConsumersRef.current.set(dataConsumer.id, dataConsumer);

          dataConsumer.on('transportclose', () => {
            dataConsumersRef.current.delete(dataConsumer.id);
          });

          dataConsumer.on('open', () => {
            console.debug('DataConsumer "open" event');
          });

          dataConsumer.on('close', () => {
            console.warn('DataConsumer "close" event');

            dataConsumersRef.current.delete(dataConsumer.id);

          });

          dataConsumer.on('error', (error: any) => {
            console.error('DataConsumer "error" event:%o', error);

          });

          dataConsumer.on('message', (message: ArrayBuffer) => {
            console.debug(
              'DataConsumer "message" event [streamId:%d]',
              dataConsumer.sctpStreamParameters.streamId
            );

            // TODO: For debugging.
            //@ts-ignore
            window.DC_MESSAGE = message;

            if (message instanceof ArrayBuffer) {
              const view = new DataView(message);
              //@ts-ignore
              const number = view.getUint32();

              if (number == Math.pow(2, 32) - 1) {
                console.warn('dataChannelTest finished!');

                nextDataChannelTestNumber.current = 0;

                return;
              }

              if (number > nextDataChannelTestNumber.current) {
                console.warn(
                  'dataChannelTest: %s packets missing',
                  number - nextDataChannelTestNumber.current
                );
              }

              nextDataChannelTestNumber.current = number + 1;

              return;
            } else if (typeof message !== 'string') {
              console.warn('ignoring DataConsumer "message" (not a string)');

              return;
            }

            switch (dataConsumer.label) {
              case 'chat': {
                // 临时注释掉store相关代码，因为当前store结构中没有peers属性
                // const { peers } = store.getState();
                // const peersArray = Object.keys(peers).map(
                //   _peerId => peers[_peerId]
                // );
                // const sendingPeer = peersArray.find(peer =>
                //   peer.dataConsumers.includes(dataConsumer.id)
                // );

                // if (!sendingPeer) {
                //   logger.warn('DataConsumer "message" from unknown peer');

                //   break;
                // }

                // store.dispatch(
                //   requestActions.notify({
                //     title: `${sendingPeer.displayName} says:`,
                    // text: message,
                // timeout: 5000,
                // })
                // );

                break;
              }

              case 'bot': {
                // store.dispatch(
                //   requestActions.notify({
                //     title: 'Message from Bot:',
                //     text: message,
                //     timeout: 5000,
                //   })
                // );

                break;
              }
            }
          });

          // TODO: REMOVE
          //@ts-ignore
          window.DC = dataConsumer;

        } catch (error) {
          console.error('"newDataConsumer" request failed:%o', error);



          throw error;
        }

        break;
      }
    }
  }


  useEffect(() => {
    createRecvTransport();

  }, [protooPeer, device]);

  return { recvTransportRef, createConsumer };
};
